package com.tuniu.agents.common.filter;

import com.tuniu.agents.advisor.ChatEnvironmentMemory;
import com.tuniu.agents.common.AgentsConstants;
import com.tuniu.agents.common.IdGenerator;
import com.tuniu.agents.common.entity.ClientInfo;
import com.tuniu.agents.util.JsonUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;

/**
 * Conversation Filter
 */
@Slf4j
public class ConversationFilter extends OncePerRequestFilter {
    private final IdGenerator idGenerator;

    private final ChatEnvironmentMemory chatEnvironmentMemory;

    public ConversationFilter(IdGenerator idGenerator, ChatEnvironmentMemory chatEnvironmentMemory) {
        this.idGenerator = idGenerator;
        this.chatEnvironmentMemory = chatEnvironmentMemory;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        // Get conversation_id from request parameters
        String conversationId = request.getHeader(AgentsConstants.AgentsConversationEnvironmentKey.CONVERSATION_ID);
        if (StringUtils.isBlank(conversationId)) {
            conversationId = request.getParameter(AgentsConstants.AgentsConversationEnvironmentKey.CONVERSATION_ID);
        }

        // If there is no conversation_id, create a new one
        if (StringUtils.isEmpty(conversationId)) {
            conversationId = idGenerator.generateId();

            // Add conversation_id to request attributes for Controller access
            request.setAttribute(AgentsConstants.AgentsConversationEnvironmentKey.CONVERSATION_ID, conversationId);

            Map<String, Object> chatInitEnvironment = new HashMap<>();
            // Set session initialization time
            chatInitEnvironment.put(AgentsConstants.AgentsConversationEnvironmentKey.CURRENT_DATETIME, LocalDateTime.now()
                    .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            chatInitEnvironment.put(AgentsConstants.AgentsConversationEnvironmentKey.PARAM_DEPARTURE_DATE, LocalDate.now().plusDays(1).toString());
            chatInitEnvironment.put(AgentsConstants.AgentsConversationEnvironmentKey.CONVERSATION_ID, conversationId);
            chatEnvironmentMemory.add(conversationId, chatInitEnvironment);

            // Here you can execute other session-related methods
            processConversation(conversationId);

            request.setAttribute(AgentsConstants.AgentsConversationEnvironmentKey.CONVERSATION_ID, conversationId);
            // Add conversation_id to response headers for frontend access
            response.setHeader(AgentsConstants.AgentsConversationEnvironmentKey.CONVERSATION_ID, conversationId);

            // Store global variables attached to the request, excluding null values
            String cParam = request.getParameter(AgentsConstants.FrontendParamKey.PARAM_ROOT_KEY);
            // Parse JSON string from request parameters
            if (cParam != null) {
                ClientInfo clientInfo = JsonUtil.fromJson(cParam, ClientInfo.class);
                Map<String, Object> env = new HashMap<>();
                // Default city is Shanghai
                env.put(AgentsConstants.AgentsConversationEnvironmentKey.PARAM_DEPARTURE_CITY_NAME,
                        StringUtils.isEmpty(clientInfo.getCn()) ? AgentsConstants.FrontendParamKey.DEFAULT_VALUE_CITY_KEY : clientInfo.getCn());
                env.put(AgentsConstants.AgentsConversationEnvironmentKey.PARAM_DEPARTURE_CITY_CODE,
                        clientInfo.getCc() == null ? AgentsConstants.FrontendParamKey.DEFAULT_VALUE_CITY_CODE : clientInfo.getCc());
                env.put(AgentsConstants.AgentsConversationEnvironmentKey.CLIENT_TYPE,
                        clientInfo.getCt() == null ? AgentsConstants.FrontendParamKey.DEFAULT_CLIENT_TYPE : clientInfo.getCt());

                if (Objects.nonNull(clientInfo.getLat())) {
                    env.put(AgentsConstants.AgentsConversationEnvironmentKey.PARAM_USER_LAT, clientInfo.getLat());
                }
                if (Objects.nonNull(clientInfo.getLng())) {
                    env.put(AgentsConstants.AgentsConversationEnvironmentKey.PARAM_USER_LNG, clientInfo.getLng());
                }
                if (clientInfo.getUcc() != null) {
                    env.put(AgentsConstants.AgentsConversationEnvironmentKey.PARAM_USER_CITY_CODE, clientInfo.getUcc());
                }
                if (StringUtils.isNotEmpty(clientInfo.getUcn())) {
                    env.put(AgentsConstants.AgentsConversationEnvironmentKey.PARAM_USER_CITY_NAME, clientInfo.getUcn());
                }
                chatEnvironmentMemory.add(conversationId, env);
            }
        }

        // Continue executing the filter chain
        filterChain.doFilter(request, response);
    }

    private void filterNullValues(Map<String, Object> map) {
        Iterator<Map.Entry<String, Object>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> entry = iterator.next();
            if (entry.getValue() == null) {
                iterator.remove();
            }
        }
    }

    private void processConversation(String conversationId) {
        // TODO Other content
        log.info("Processing conversation: {}" , conversationId);
    }
}
